package org.molamil.acto.pages {
	import flash.events.EventDispatcher;
	import flash.errors.IllegalOperationError;
	import flash.utils.getQualifiedClassName;

	import org.molamil.acto.core.ns.acto;
	import org.molamil.acto.core.processors.Processor;
	import org.molamil.acto.core.loggers.Logger;

	/**
	 * XXX: Document.
	 * 
	 * @author Jorge Hernandez
	 */
	public class AbstractPageBlockFactory extends EventDispatcher implements PageBlockFactory {

		
		// PROPERTIES
		
		private static var log:Logger = Logger.getLogger("PageBlockFactory");
	
		private var pageList:Array; // of PageContext
		private var blockList:Array; // of BlockContext
		private var processors:Array; // of Processor
	
	
		// CONSTRUCTOR
		
		public function AbstractPageBlockFactory(pageList:Array = null, blockList:Array = null) {
			
			// Abstract check.
			if (getQualifiedClassName(this) == "org.molamil.acto.pages::AbstractPageBlockFactory")
        		throw new IllegalOperationError("AbstractPageBlockFactory must be extended");
			
			this.pageList = new Array();
			if (pageList != null) {
				for each (var pageContext:PageContext in pageList)
					createPage(pageContext, false);
			}
			
			this.blockList = new Array();
			if (blockList != null) {
				for each (var blockContext:BlockContext in blockList)
					createBlock(blockContext, false);
			}
			
			processors = new Array();
			
		}
		
	
		// PUBLIC METHODS
		
		public function createPage(context:PageContext, checkConsistency:Boolean = true):void {
			
			//TODO: Add checks for minimum required information.
			//TODO: Set defaults.
			
			if (context.id == null) {
				log.warning("The context for the page to create needs to have a valid id, the new page will not be created.");
				return;
			}
			
			if (containsPage(context.id)) {
				log.warning("There is already a page with id \"{id}\", the new page will not be created.".replace("{id}", context.id));
				return;
			}
			
			pageList.push(context);
			
			if (checkConsistency)
				acto::runProcessors();
			
			var event:PagesEvent = new PagesEvent(PagesEvent.PAGE_CONTEXT_CREATED, false, false, null, context);
			dispatchEvent(event);
			
		}
		
		public function removePage(id:String, checkConsistency:Boolean = true):void {
			//TODO: Implement.
		}
		
		public function containsPage(id:String):Boolean {
			var b:Boolean = getPage(id) != null;
			log.debug("containsPage(), id = " + id + ": " + b);
			return b;
		}
		
		public function getPage(id:String):PageContext {
			
			log.debug("getPage(), id = " + id);
		
			var pageContext:PageContext = null;
			
			for (var i:uint = 0; i < pageList.length; i++) {
				var pc:PageContext = pageList[i];
				if (pc.id == id) {
					pageContext = pc;
					break;
				}
			}
			
			if (pageContext == null)
				log.info("The requested page with id '" + id + "' does not exist");
				
			return pageContext;
		
		}
		
		public function getPages():Array {
			log.debug("getPages(), returning " + pageList.length);
			return pageList;
		}
		
		public function getPagesAt(level:Number):Array {
			
			log.debug("getPagesAt(), level = " + level);
		
			var a:Array = new Array();
			
			for (var i:uint = 0; i < pageList.length; i++) {
				var pageContext:PageContext = pageList[i];
				if (pageContext.level == level)
					a.push(pageContext);
			}
						
			return a;
		
		}
		
		public function createBlock(context:BlockContext, checkConsistency:Boolean = true):void {
			
			//TODO: Add checks for minimum required information.
			//TODO: Set defaults.
			
			if (context.id == null) {
				log.warning("The context for the block to create needs to have a valid id, the new block will not be created.");
				return;
			}
			
			if (containsBlock(context.id)) {
				log.warning("There is already a block with id \"{id}\", the new block will not be created.".replace("{id}", context.id));
				return;
			}
			
			blockList.push(context);
			
			if (checkConsistency)
				acto::runProcessors();
				
			var event:PagesEvent = new PagesEvent(PagesEvent.BLOCK_CONTEXT_CREATED, false, false, null, context);
			dispatchEvent(event);
			
		}

		public function removeBlock(id:String, checkConsistency:Boolean = true):void {
			//TODO: Implement.
		}
		
		public function containsBlock(id:String):Boolean {
			var b:Boolean = getBlock(id) != null;
			log.debug("containsBlock(), id = " + id + ": " + b);
			return b;
		}
		
		public function getBlock(id:String):BlockContext {
			
			log.debug("getBlock(), id = " + id);
		
			var blockContext:BlockContext = null;
			
			for (var i:uint = 0; i < blockList.length; i++) {
				var bc:BlockContext = blockList[i];
				if (bc.id == id) {
					blockContext = bc;
					break;
				}
			}
			
			if (blockContext == null)
				log.info("The requested block with id '" + id + "' does not exist");
				
			return blockContext;
		
		}
		
		public function getBlocks():Array {
			log.debug("getBlocks(), returning " + blockList.length);
			return blockList;
		}
		
		acto function addProcessor(processor:Processor):void {
			processors.push(processor);
			processor.process(this);
		}
		
		acto function removeProcessor(processor:Processor):void {
			//TODO: Implement.
		}
		
		acto function runProcessors():void {
			for each (var processor:Processor in processors)
				processor.process(this);
		}
		
		
	}
	
}
